package org.moyrax.nereyda.services.core
{
	import org.moyrax.nereyda.services.errors.MappingExistsError;
	import org.moyrax.nereyda.services.errors.ServiceMessages;
	import org.moyrax.nereyda.services.template.IMappingController;
	import org.moyrax.nereyda.services.template.IServiceObject;
	
	import flash.net.getClassByAlias;
	import flash.net.registerClassAlias;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;
	
	import mx.collections.ArrayCollection;
	
	public class FrontMapper implements IMappingController
	{
		private var mapping :ArrayCollection = new ArrayCollection();
		
		/**
		 * Vincula una Clase de Servicio con un WebService determinado.
		 * Cuando se reciba la respuesta del servidor, automáticamente
		 * se creará una nueva instancia del mismo y será inicializado
		 * con los datos devueltos por el servicio invocado. Opcionalmente
		 * se puede especificar el evento que se desencadenará cuando
		 * culmine el mapeo del objeto.
		 * 
		 * @param serviceName   Nombre del Servicio.
		 * @param serviceObject Clase de Servicio vinculada.
		 * @param event         Evento relacionado.
		 */
		public function mapObject( serviceName:String, serviceObject:Class, serviceEvent:Class = null, replaceMap:Boolean = false ):void
		{
			if ( this.mappingExists(serviceName, serviceObject) && !replaceMap )
			{
				throw MappingExistsError( ServiceMessages.MAPPING_EXISTS );
			}
			
			var object :String = getQualifiedClassName( new serviceObject() );
			var event  :String = getQualifiedClassName( new serviceEvent(null) );
			
			registerClassAlias( object, serviceObject );
			registerClassAlias( event, serviceEvent );
			
			
			mapping.addItem(
				new MappingEntry( serviceName, object, event)
			);
		}
		
		/**
		 * Devuelve la clase vinculada al servicio especificado.
		 * 
		 * @param serviceName   Nombre del Servicio.
		 */
		public function getObjects( serviceName:String ):ArrayCollection
		{
			var result :ArrayCollection = new ArrayCollection;
			
			for each ( var item:MappingEntry in mapping ) {
				if ( item.service == serviceName )
					result.addItem( item );
			}
			
			if ( result.length == 0 ) {
				result = null;
			}
				
			return( result );
		}

		/**
		 * Elimina el mapeo de un Objeto de Servicio
		 * al servicio especificado.
		 * 
		 * @param serviceName   Nombre del Servicio.
		 * @param serviceObject Clase de Servicio vinculada.
		 */
		public function removeMapping( serviceName:String, serviceObject:Class = null ):void
		{
			var result :ArrayCollection = new ArrayCollection;
			
			for each ( var item:MappingEntry in mapping ) {
				if ( (item.service == serviceName) &&
				     (getDefinitionByName(item.object) == serviceObject) ) {
				      	
					var index :int = mapping.getItemIndex( item );
					
					mapping.removeItemAt( index );
				}
			}
		}
		
		/**
		 * Determina si el objeto especificado ya
		 * fue vinculado a un servicio determinado.
		 * 
		 * @param serviceName   Nombre del Servicio.
		 * @param serviceObject Clase de Servicio vinculada.
		 */
		public function mappingExists( serviceName:String, serviceObject:Class ):Boolean
		{
			var result :ArrayCollection = this.getObjects( serviceName );
			
			if ( !result ) {
				return(false);
			}
			
			for each ( var item:MappingEntry in result )
			{
				if ( (item.service == serviceName) &&
				     (getDefinitionByName(item.object) == serviceObject) )
				{
					return( true );
				}
			}
			
			return( false );
		}

		/**
		 * Devuelve el evento vinculado al objeto especificado.
		 * 
		 * @param serviceObject Objeto del que se desea obtener el evento.
		 */
		public function getObjectEvent( serviceObject:IServiceObject ):Class
		{
			var className :String;
			var objClass  :Class;
			
			try {
				for each ( var item:MappingEntry in mapping ) 
				{
					className = getQualifiedClassName( serviceObject );
					
					if ( item.object == className )
						return( getClassByAlias(item.event) );
				}
			}
			catch(error:Error) {}
			
			return( null );
		}
		
		/**
		 * Devuelve un valor que indica si el objeto
		 * especificado tiene vinculado un evento.
		 * 
		 * @param serviceObject Objeto que se analizará.
		 */
		public function objectHasEvent( serviceObject:IServiceObject ):Boolean
		{
			return (
				this.getObjectEvent( serviceObject ) ? true : false
			);
		}
	}
}